package cn.sylinx.hbatis.ext.tool;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import cn.sylinx.hbatis.db.mapper.ModelBuilder;
import cn.sylinx.hbatis.db.mapper.anno.BlobType;
import cn.sylinx.hbatis.db.mapper.anno.ColumnDesc;
import cn.sylinx.hbatis.db.mapper.anno.TextType;
import cn.sylinx.hbatis.kit.StrKit;
import cn.sylinx.hbatis.plugin.model.ModelFabric;

public class MysqlRepositoryUtil {

	public static final String MYSQL_SINGLE_QUOTES = "`";

	public static final String MYSQL_DROP_TEMPLATE = "DROP TABLE IF EXISTS `%s`";

	public static final String MYSQL_CREATE_TEMPLATE = "CREATE TABLE `%s` (";

	public static final String MYSQL_ROW_PK = " PRIMARY KEY (`id`) ";

	public static final String MYSQL_ENGINE = ") ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin";

	public static final String MYSQL_ALTER_TABLE = "ALTER TABLE `%s`";

	public static final String MYSQL_ALTER_TABLE_ADD_COL = "ADD COLUMN";

	public static final String MYSQL_ALTER_TABLE_MODIFY_COL = "MODIFY COLUMN";

	protected final static Map<String, String> MYSQL_JAVA_JDBC_MAP = new HashMap<String, String>();

	static {

		MYSQL_JAVA_JDBC_MAP.put("java.util.Date", "datetime");
		MYSQL_JAVA_JDBC_MAP.put("java.lang.String", "varchar");
		MYSQL_JAVA_JDBC_MAP.put("java.lang.String.MIN", "text");
		MYSQL_JAVA_JDBC_MAP.put("java.lang.String.MEDIUM", "mediumtext");
		MYSQL_JAVA_JDBC_MAP.put("java.lang.String.LONG", "longtext");
		MYSQL_JAVA_JDBC_MAP.put("java.lang.Long", "bigint");
		MYSQL_JAVA_JDBC_MAP.put("java.lang.Byte", "tinyint");
		MYSQL_JAVA_JDBC_MAP.put("java.lang.Short", "smallint");
		MYSQL_JAVA_JDBC_MAP.put("java.math.BigInteger", "bigint(20) unsigned");
		MYSQL_JAVA_JDBC_MAP.put("java.math.BigDecimal", "decimal");
		MYSQL_JAVA_JDBC_MAP.put("java.lang.Integer", "int");
		MYSQL_JAVA_JDBC_MAP.put("java.lang.Float", "float");
		MYSQL_JAVA_JDBC_MAP.put("java.lang.Double", "double");
		MYSQL_JAVA_JDBC_MAP.put("java.lang.Boolean", "bit(1) DEFAULT b'0'");
		MYSQL_JAVA_JDBC_MAP.put("[B", "blob");
		MYSQL_JAVA_JDBC_MAP.put("[B.MIN", "blob");
		MYSQL_JAVA_JDBC_MAP.put("[B.MEDIUM", "mediumblob");
		MYSQL_JAVA_JDBC_MAP.put("[B.LONG", "longblob");
	}

	private Class<? extends Object> clz;
	private List<Field> fields;
	private String tableName;
	private String tableDesc;
	private Map<String, String> attrMapping;
	private Map<String, ColumnDesc> fieldDesc;
	private Map<String, Field> fieldMap;

	public MysqlRepositoryUtil(Class<? extends Object> clz) {

		this.clz = clz;

		if (clz == null) {
			throw new RuntimeException("clz is null or path is null");
		}

		ModelFabric modelFabric = ModelBuilder.getModelFabric(this.clz);
		fields = modelFabric.getFields();
		tableName = modelFabric.getTableName();
		tableDesc = modelFabric.getTable().desc();
		attrMapping = modelFabric.getAttrMapping();
		fieldDesc = modelFabric.getFieldDesc();
		fieldMap = modelFabric.getFieldMap();

	}

	public String generateAddTableColumnDDL(String prop) {

		StringBuilder sb = new StringBuilder();
		sb.append(String.format(MYSQL_ALTER_TABLE, tableName)).append(" ").append(MYSQL_ALTER_TABLE_ADD_COL)
				.append(" ");
		String propertyName = prop;
		Field field = fieldMap.get(prop);
		ColumnDesc cd = fieldDesc.get(propertyName);
		String javaType = getFieldType(field);

		String jdbcType = toJdbcType(javaType);
		String columName = attrMapping.get(propertyName);
		if (StrKit.isBlank(columName)) {
			columName = propertyName;
		}

		String column_template = MYSQL_SINGLE_QUOTES + "%s" + MYSQL_SINGLE_QUOTES + " %s;\n";

		int len = 0, precision = 0;
		boolean blob = false, text = false;
		BlobType bt = BlobType.MIN;
		TextType tt = TextType.MIN;
		
		String comment = null;
		boolean nullable = true;

		if (cd != null) {
			len = cd.len();
			precision = cd.precision();
			comment = cd.desc();
			nullable = cd.nullable();
			blob = cd.blob();
			text = cd.text();
			bt = cd.blobType();
			tt = cd.textType();
		}

		if (blob) {
			// 二进制判断
			String key = javaType + "." + bt.name();
			jdbcType = toJdbcType(key);
		} else if (text) {
			// 文本
			String key = javaType + "." + tt.name();
			jdbcType = toJdbcType(key);
		} else if (len > 0) {
			jdbcType = jdbcType + (precision == 0 ? "(" + len + ")" : "(" + len + "," + precision + ")");
		} else if ("varchar".equals(jdbcType)) {
			jdbcType = jdbcType + "(50)";
		}

		// 空判断
		jdbcType = jdbcType + (nullable ? " DEFAULT NULL" : " NOT NULL");

		// COMMENT
		if (StrKit.isNotBlank(comment)) {
			jdbcType = jdbcType + " COMMENT '" + comment + "'";
		}

		if ("id".equals(field.getName())) {
			// id主键
			jdbcType = "bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键'";
		}

		sb.append(String.format(column_template, columName, jdbcType));

		return sb.toString();
	}

	public String generateModifyTableColumnDDL(String prop) {

		StringBuilder sb = new StringBuilder();
		sb.append(String.format(MYSQL_ALTER_TABLE, tableName)).append(" ").append(MYSQL_ALTER_TABLE_MODIFY_COL)
				.append(" ");
		String propertyName = prop;
		Field field = fieldMap.get(prop);
		ColumnDesc cd = fieldDesc.get(propertyName);
		String javaType = getFieldType(field);

		String jdbcType = toJdbcType(javaType);
		String columName = attrMapping.get(propertyName);
		if (StrKit.isBlank(columName)) {
			columName = propertyName;
		}

		String column_template = MYSQL_SINGLE_QUOTES + "%s" + MYSQL_SINGLE_QUOTES + " %s;\n";

		int len = 0, precision = 0;
		boolean blob = false, text = false;
		BlobType bt = BlobType.MIN;
		TextType tt = TextType.MIN;
		
		String comment = null;
		boolean nullable = true;

		if (cd != null) {
			len = cd.len();
			precision = cd.precision();
			comment = cd.desc();
			nullable = cd.nullable();
			blob = cd.blob();
			text = cd.text();
			bt = cd.blobType();
			tt = cd.textType();
		}

		if (blob) {
			// 二进制判断
			String key = javaType + "." + bt.name();
			jdbcType = toJdbcType(key);
		} else if (text) {
			// 文本
			String key = javaType + "." + tt.name();
			jdbcType = toJdbcType(key);
		} else if (len > 0) {
			jdbcType = jdbcType + (precision == 0 ? "(" + len + ")" : "(" + len + "," + precision + ")");
		} else if ("varchar".equals(jdbcType)) {
			jdbcType = jdbcType + "(50)";
		}

		// 空判断
		jdbcType = jdbcType + (nullable ? " DEFAULT NULL" : " NOT NULL");

		// COMMENT
		if (StrKit.isNotBlank(comment)) {
			jdbcType = jdbcType + " COMMENT '" + comment + "'";
		}

		if ("id".equals(field.getName())) {
			// id主键
			jdbcType = "bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键'";
		}

		sb.append(String.format(column_template, columName, jdbcType));

		return sb.toString();
	}

	/**
	 * 生成表的SQL
	 * 
	 * @return
	 */
	public String generateSql() {

		StringBuilder sb = new StringBuilder();
		sb.append(String.format(MYSQL_CREATE_TEMPLATE, tableName)).append("\n");
		List<Field> sortField = new ArrayList<Field>();
		for (Field field : fields) {
			if (field.getName().equals("id")) {
				sortField.add(0, field);
			} else {
				sortField.add(field);
			}
		}
		for (Field field : sortField) {
			sb.append(generateSingleColumn(field)).append("\n");
		}
		sb.append(MYSQL_ROW_PK).append("\n");
		sb.append(MYSQL_ENGINE).append(StrKit.isBlank(tableDesc) ? "" : (" COMMENT='" + tableDesc + "'")).append(";\n");

		return sb.toString();
	}

	private String generateSingleColumn(Field field) {

		StringBuilder sb = new StringBuilder();

		String propertyName = field.getName();
		ColumnDesc cd = fieldDesc.get(propertyName);
		String javaType = getFieldType(field);

		String jdbcType = toJdbcType(javaType);
		String columName = attrMapping.get(propertyName);
		if (StrKit.isBlank(columName)) {
			columName = propertyName;
		}

		String column_template = MYSQL_SINGLE_QUOTES + "%s" + MYSQL_SINGLE_QUOTES + " %s,";

		int len = 0, precision = 0;
		boolean blob = false, text = false;
		BlobType bt = BlobType.MIN;
		TextType tt = TextType.MIN;

		String comment = null;
		boolean nullable = true;

		if (cd != null) {
			len = cd.len();
			precision = cd.precision();
			comment = cd.desc();
			nullable = cd.nullable();
			blob = cd.blob();
			text = cd.text();
			bt = cd.blobType();
			tt = cd.textType();
		}

		if (blob) {
			// 二进制判断
			String key = javaType + "." + bt.name();
			jdbcType = toJdbcType(key);
		} else if (text) {
			// 文本
			String key = javaType + "." + tt.name();
			jdbcType = toJdbcType(key);
		} else if (len > 0) {
			jdbcType = jdbcType + (precision == 0 ? "(" + len + ")" : "(" + len + "," + precision + ")");
		} else if ("varchar".equals(jdbcType)) {
			jdbcType = jdbcType + "(50)";
		}

		// 空判断
		jdbcType = jdbcType + (nullable ? " DEFAULT NULL" : " NOT NULL");

		// COMMENT
		if (StrKit.isNotBlank(comment)) {
			jdbcType = jdbcType + " COMMENT '" + comment + "'";
		}

		if ("id".equals(field.getName())) {
			// id主键
			jdbcType = "bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键'";
		}

		sb.append(String.format(column_template, columName, jdbcType));

		return sb.toString();
	}

	/**
	 * 获取javaType对应的 jdbcType
	 * 
	 * @param javaType
	 * @return
	 */
	protected String toJdbcType(String javaType) {

		return MYSQL_JAVA_JDBC_MAP.get(javaType);
	}

	/**
	 * 获取字段类型
	 * 
	 * @param field
	 * @return
	 */
	protected String getFieldType(Field field) {

		return field.getType().getName();
	}

	public List<Field> getFields() {
		return fields;
	}

	public String getTableName() {
		return tableName;
	}

	public void setTableName(String tableName) {
		this.tableName = tableName;
	}
}